Dylan Application
Volume Number: 10
Issue Number: 9
Column Tag: Apple Technology First Look
Writing An Application In Dylan 
Looking ahead to a richer programming experience
By Larisa Matejic, Brown University
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
Conceived and developed in Cambridge, Massachusetts, Dylan holds the promise of
a new breed of development environment - dynamic, interactive, object oriented, and
aimed at the professional application developer. As of this writing, the Dylan team
continues to prepare Dylan for release. We’re pleased to be able to bring you this first
look at developing an application in Dylan. Our author has worked with Dylan over the
past year or so as an intern with Apple’s Cambridge group, and walks us through using
Dylan to construct a QuickTime movie player. While we’ll bring you more articles on
things like language specifics and the highly-interactive nature of the development
environment later, this article should help you get a feel for putting together an
application with Dylan - Ed stb
Dylan is one of the most versatile languages I have encountered. Apple Dylan has
a number of features that make it easy and natural to use. Apple Dylan includes a
development environment which greatly simplifies programming and debugging.
Dylan’s expressive, yet simple syntax lends itself to efficient application development.
In this article, we’ll look at the process of writing a movie player application to
explore Dylan’s unique features. We’ll focus on different properties of the Dylan
language, as well as Apple Dylan’s cross-language calling mechanism.
The movie player application we’ll build mimics the QuickTime Movie Player
application provided in the QuickTime developer’s kit.
QuickTime API
To use QuickTime, we’ll need to import QuickTime function and type definitions
into Dylan. In fact, we’ll need to do the same for any Toolbox function or type we use in
writing our application.
Dylan makes it easy to interface with C code - simply specify the .h file name and
list the names of the functions and types you want to use. Dylan takes care of the rest.
Here’s an example of how to import Toolbox function headers and types:
/* 1 */
define interface
#include "Quickdraw.h",
import: {"Picture", "PicHandle",
"DrawPicture", "KillPicture",
"CGrafPort", "UnionRect", "GDHandle",
"GWorldPtr
}
end interface;
We can call any of the C functions we import in the scope of our application as if
they were written in Dylan.
Dylan Framework API
Dylan Framework provides useful class definitions for writing applications; it
consists mainly of Dylan derivatives of Toolbox functionality. Among other things, it
handles events, pick correlation (a fancy buzzword), view, and window operations. It
originated with MacApp, but is cleaner and written entirely in Dylan.
The next thing is to get QuickTime and the Framework together. For that, we’ll
define classes to deal with QuickTime movies. First, a bit about how classes work in
Dylan.
Class Definitions
As in other object-oriented languages, each instance of a Dylan class stores
information about its state in slots. A slot, equivalent to a data-member or field in
other object-oriented languages, is a place to keep data, and each instance gets its own
storage (i.e. set of slots). In addition to having its own slots, a class inherits all the
slots of its superclasses.
Slots in Dylan are accessed by invoking methods using a simple syntax. For
instance, to get the value stored in slot v of some object point, we write: point.v.
We’ll define a class called , which will provide a view onto the
QuickTime movie. Note: it’s a convention in Dylan to surround class names by angle
brackets.
Our class inherits from the Framework’s . Among other
things, it handles mouse events in the view. In other words, is a
subclass of , and we’ll add the data and methods we need to work with a
QuickTime movie. We include slots particular to in the class definition;
it inherits other slots from .
/* 2 */
define class ()
slot movie :: ,
init-value: as(, 0),
init-keyword: movie:;
slot show-mc?,
init-value: #t,
init-keyword: show-mc?:;
slot enable-editing?,
init-value: #t,
init-keyword: enable-editing?:;
slot is-playing?,
init-value: #f;
slot loop?,
init-value: #f;
slot mc :: ,
init-value: as(, 0);
end class;
From the class definition above, we see that the slot movie is of type ,
imported from QuickTime through the define interface. Its initial value is a null
pointer, or, more accurately, it is the integer 0 coerced into type by the as
operator. To specify which movie to associate with a particular , we’ll
use the init-keyword at instantiation time. In this case, the init-keyword is movie:.
The next slot in is a boolean called show-mc?, whose value
indicates whether to show QuickTime’s movie controller in the .
Similarly, the boolean slot enable-editing? indicates whether the movie is read-only.
The next two slots can’t be initialized by use of a keyword by the creator of an
instance of . That’s because we chose to not include an init-keyword in
their slot definitions. Rather, their values are determined in the initialize routine of
the instance of .
The slot is-playing? is used to keep track of when the movie is playing when the
movie controller is not present to do that for us.
The slot mc is of the type , a type also imported from
QuickTime. This is where we store a pointer to the movie controller object. The movie
controller provides an interface to the movie, making it possible to play, stop and edit
the movie.
With the QuickTime Movie Player, a movie is associated with a window that
contains it, that window’s controls, and the file the movie is stored in. We need to
define a class that will provide that window and the file. The Dylan Framework class
provides a mechanism for exactly that. We’ll define a subclass of
called in the following way:
/* 3 */
define class ()
slot movie :: ,
init-value: as(, 0);
slot movie-view :: ;
end class;
In addition to inheriting all the slots of , has a slot
for storing an instance of and a slot for storing a pointer to the movie.
The slot movie stores a copy of the pointer to the QuickTime movie already stored in
. Having the slot movie present in is a matter of
convenience and can be omitted if desired.
Now that we have defined our main classes, we can address how to define methods
for these classes to provide the functionality want in this application.
Our app’s File and Edit menus will be standard. We’ll also have a Movie menu
with items like Set Movie Poster, Show Movie Poster and Loop. We’ll focus our
discussion on implementing the functionality of the items in the File menu, mainly
Opening and Closing a movie. The code at the end of the article shows how we
implemented the functionality of the rest of the menu items.
We aren’t going to show how to create and add menu items to a menu here. What
we will discuss is the functionality needed for the menu callbacks, i.e. the functions
that are called as a result of a menu item being selected.
Methods in Dylan
Dylan methods are grouped by name into collections called generic functions.
Dylan supports polymorphism through generic functions and method dispatch. When
you call a generic function, Dylan determines which method to invoke based on the type
of arguments passed to it. This process is called method dispatch.
A method is said to be specialized when it is defined to operate on instances of a
specific class. That is, a method is specialized on the type of its parameters. In
addition to providing type information to the compiler, specializers make code easier
for humans to understand. During method dispatch, a generic function compares all
specializers with the arguments it receives to determine which method to invoke. The
method whose parameters most closely match the argument types gets invoked.
You don’t need to modify class definitions to modify an object’s behavior. Method
definitions in Dylan are separate from the classes they use for specialization. This
makes for more flexible code organization. Given that you may not even be able to get
to the code where a class is defined, the most effective approach then seems to be adding
a method specialized on a specific class. For instance, Dylan lets you add a method on
the class without changing the class itself or affecting other applications.
Next we’ll discuss some of the functions of our movie player app, and Dylan’s
modularity will become more apparent. We’ll also show how simple it is to use
pre-defined classes and augment them to get the behavior you want.
Opening a Movie File
When the user selects Open from the File menu and chooses a QuickTime movie,
we’ll make an instance of , and store the file pointer in the
main-file slot.
Since is a subclass of , we can specialize the
methods for to incorporate our movie specific functionality.
The initialize method for calls the method open- files, which opens a
QuickTime movie file:
/* 4 */
define method open-files (document :: )
open-movie-file(document.main-file);
end method;
define method open-movie-file (file :: )
with-stack-structure
(spec(),
begin
set-fs-spec(file, spec);
let (err, ref-num) =
OpenMovieFile(spec, file.data-permission);
fail-os-error(err);
file.data-ref-num := ref-num;
end);
end method;
To get the movie data out of the file, we specialize read-from-file, a method of
, that is eventually called by initialize.
/* 5 */
define method read-from-file (document :: ,
file :: )
let (err, new-movie,resId) =
NewMovieFromFile( file.data-ref-num, "", 0);
ignore(resId);
fail-os-error(err);
document.movie := new-movie;
close- files(document);
end method;
Next, in instantiating , initialize calls the method make- windows to
open the document in a window on screen.
/* 6 */
define method make- windows (document :: )
let movie-bounds =
with-stack-structure(box(),
begin
GetMovieBox(document.movie,box);
get-rect(box);
end);
let movie-size = point(movie-bounds.width, movie-bounds.height);
document.movie-view := make(,
location: $zero-point,
size: movie-size);
let window = make(< window>,
location: point(100, 100),
size: movie-size,
next- handler: document,
target-view: document.movie-view,